ソフトウェアアーキテクチャの基礎輪読会 3章
日付:11/6
章:3章 モジュール性
調査者:takasshii.icon
注意
3章の内容をtakasshii.iconなりに分解して解説しているため、本文と対応していない箇所が多々あります。
かなりモリモリです。でも重要。
モジュール性
モジュールの概念は曖昧
本書の中での一貫性を保つための独自の定義をする
モジュールについて理解するのは重要
ソフトウェアアーキテクチャの言説の95%はモジュール性の利点を賞賛するために費やされる
アーキテクチャを分析する際に使う多くのツールはモジュール性の概念に依存している
メトリクス、適応度関数、可視化など
要素同士の繋がりに注意を払わずにシステムを設計すると、数え切れない困難が生まれる
ソフトウェアはエントロピーが増大する傾向があるため
優れたモジュール性を維持することは暗黙的なアーキテクチャ特性である
モジュールの定義
「関連するコードを論理的にグループ化すること」をモジュール
ex. (パラダイム)クラス集合、関数集合
ex. (言語)Java → パッケージ, .NET
近年ではメタオブジェクトプロトコルという概念も
(複数のパッケージが密に結合されていると、再利用するのが難しい)
論理的な概念として扱う
名前空間と技術的な実装は別
凝集度
モジュールの要素がどの程度モジュールに収まっているかの度合いを指す
関連する要素が全て1箇所にまとまっている状態が凝集したモジュールの理想
モジュールのまとまりを細かくすると、モジュール間の呼び出しが必要になる
凝集度の高いモジュールを分割しようとしても、結合が増えて可読性が低下するだけだ
takasshii.iconAppifyの人のスライドがわかりやすい takasshii.icon森さんの記事がわかりやすいのでスライドを使って説明するよ 凝集度の尺度
https://assets.st-note.com/production/uploads/images/50757950/picture_pc_1a45543ccf3223225d72d3726faaa1f4.jpg?width=800
https://assets.st-note.com/production/uploads/images/50757942/picture_pc_ab1ffb43a47685ad32b1d393b0429d0f.jpg?width=2000&height=2000&fit=bounds&quality=85
偶発的凝集
同じソースファイル内にある以外は関連性がない(最も最悪な凝集)
https://assets.st-note.com/production/uploads/images/50757932/picture_pc_759028b39a8a19fc3e1bcd194a1e4849.jpg?width=800
論理的凝集
操作の上では関係があるが、機能は異なる
ex. StringUtils Stringを操作することで関連しているが、互いには関連していない
https://assets.st-note.com/production/uploads/images/50757925/picture_pc_e90da4f8c76353219e008ad9b4160e13.jpg?width=2000&height=2000&fit=bounds&quality=85
時間的凝集
起動時に初期化しなければならない処理など時間的な凝集
https://assets.st-note.com/production/uploads/images/50757946/picture_pc_21c5ffa91a092ae52ff4050b086a89ff.jpg?width=2000&height=2000&fit=bounds&quality=85
手続き的凝集
特定の順序でコードを実行する
https://assets.st-note.com/production/uploads/images/50757923/picture_pc_bfb9f54218294b3188ee5a426cbd5357.jpg?width=2000&height=2000&fit=bounds&quality=85
通信的凝集
2つのモジュールが通信の連鎖を形成して情報を操作したり、出力に貢献したりする
ex. DBにレコードを追加 → 電子メールを生成
https://assets.st-note.com/production/uploads/images/50757909/picture_pc_3eefcf5cb97ad227114e04830867190c.jpg?width=800
逐次的凝集
一方が出力したデータをもう一方が入力とする
2つのモジュールが相互に作用している
https://assets.st-note.com/production/uploads/images/50757933/picture_pc_579c5d23657b64cfa0275f10bd2d4ab1.jpg?width=800
機能的凝集
関連する要素だけでモジュールが構成される
モジュールが機能するために不可欠なものが全て含まれている
https://assets.st-note.com/production/uploads/images/50757916/picture_pc_d48fa5e5fd8dc59e30f71fda72206dbc.jpg?width=800
リファクタについて
凝集度を上げることで読みやすいコードになる
関数は分ければいいものではない
認知負荷が高くなるため
凝集度は精度の低い指針
関数を分けるかどうかは場合による
凝集度を判断するためのより構造に優れたCKメトリクスという指針を開発してきた 結合度や循環的複雑度といった指針
凝集度の計測
LCOM
フィールドを介して結合されていないメソッド群の操作
凝集度が高い、低いが分かる
構造的に凝集度が欠如しているかが分かるだけ
結合度
takasshii.icon凝集度と一緒に使われることが多い概念
グラフ理論に基づいた概念
結合度の核となる概念
求心性結合
入力する接続数
遠心性結合
出力する接続数
takasshii.iconといってもわかんないので例 ↓
ex. class A extends BだとAはBに依存している
依存関係はA→Bとなり、BはAの出力、AはBの入力となる
結合度の具体例
takasshii.icon本に具体例がないので森さんの記事に戻って説明するよ https://assets.st-note.com/production/uploads/images/50757937/picture_pc_c2f00cd69e4d9b608cd7eb86640dd209.jpg?width=2000&height=2000&fit=bounds&quality=85
抽象度
抽象と実装の比率
抽象度 = 抽象 / (具体+抽象)
5000行のコードで1つのメソッドを持つなら 1/5001で0に近くなる
0なら抽象度が低く、1なら抽象度が高い
例 Androidで考えてみよう
抽象度の低いクラスの例(抽象クラスがないため、抽象度は0になる)
code:impl.kt
class QRCodeRepositoryImpl : QRCodeRepository() {
override suspend fun writeQRCodeResult() {}
}
抽象度の高いクラスの例(抽象クラスしかないため、抽象度は1になる)
code:interface.kt
interface QRCodeRepository() {
suspend fun writeQRCodeResult() {}
}
例題1 モジュールの抽象度を考えたい時はどうすればよい?
抽象クラスと具象クラスを用いて式を作ってみよう
不安定度
コードの不安定さを判断する
不安定な度合いが高いものは変更されたときに壊れやすくなる
不安定度 = 出力 / (出力+入力)
ex. あるクラスAが複数のクラスを呼び出している
class B extends A, class C extends AとB→A、C→Aとなっていて、Aは何も継承していない場合は、Aの出力は2, Aの入力は0となり、不安定度は1となる
つまり、多くのクラスに依存されてたら不安定になるよという意味
例 Androidで考えてみよう
https://cdn-ak.f.st-hatena.com/images/fotolife/d/delyumemori/20211122/20211122151140.png
例題2 infraモジュールの不安定度は?
例題3 appモジュールの不安定度は?
距離
主系列距離 = | 抽象度 + 不安定度 - 1 |
takasshii.icon主系列からの距離だよ!
https://cdn-ak.f.st-hatena.com/images/fotolife/d/delyumemori/20211122/20211122151236.png
無駄ゾーン
抽象的すぎる
Androidだったら
無駄ゾーンに入るモジュールは参照されてないので必要ないよね
苦痛ゾーン
抽象度が低くて(実装の拡張もしにくい)安定度が低い(変更がしにくい)ので苦痛を伴う
Androidだったら
スマートフォンアプリ開発ではどのようなモジュールがこのゾーンに含まれやすいかというと、例えば書籍でも例として上げられている具象ユーティリティライブラリーです。ほにゃららUtilクラスが作られ、アプリケーションの色々なところから呼び出される処理がそのほにゃららUtilクラスに追加されていく、というのは皆さんも心当たりがあるのではないでしょうか。こういったクラスはあまり変更されないのであれば苦痛を伴いませんが、良く変更される処理がその中に含まれているとその変更が予期しない形で他のモジュールに波及したり、あるいはどういった利用のされ方をされているか分からないので変更できなくなってしまうようなことが想定されます。
システムの全体的な正しさを維持するため、あるコンポーネントの変更が別のコンポーネントの変更を必要とする場合、2つのコンポーネントはコナーセント(接続)されている。
takasshii.iconこの記事がわかりやすいのでこれを使って説明するよ 静的なコナーセンス
結合の度合い
名前
型
意味
位置
アルゴリズム
動的なコナーセンス
実行順序
タイミング
値
アイデンティティ
どれだけリファクタリング可能か
https://gyazo.com/44501e513883792351ea193b62f62af8
コナーセンスの位置関係
モジュール同士の距離
距離の近いコード→同じモジュール内
距離の離れたコード→別々のモジュール内
距離が近いほど高度で多様なコナーセンスの形をもてる
同じコナーセンスを同じモジュールにまとめた方が良い
コナーセンスの度合い
コナーセンスの影響の大きさ
接続する箇所が多くなればコナーセンスの問題は大きくなる
モジュール性を向上させるための3つのガイド
カプセル化された要素に分割することで、コナーセンスの度合いを最小に抑える カプセル化された境界を跨ぐコナーセンスの度合いは最小に抑える
カプセル化の境界内ではコナーセンスの度合いを最大化する
ルール
度合いのルール
強いコナーセンスを弱いコナーセンスに変換
位置関係のルール
ソフトウェア要素間の距離が遠くなるにつれて弱いコナーセンスを使用する
結合とコナーセンスは共通するものである
https://gyazo.com/a753cbccab7754745a991478faa707ce
コナーセンスには問題がある
低レベルの詳細を見ていて、モジュール間の結合のような高レベルはみていない
基本的な判断に対処していない
現代のコナーセンスについての新しい考えを紹介する
モジュールからコンポーネント
例題の答え
例題1
抽象度 = 抽象 / (具体+抽象) = 抽象クラスの数 / (抽象クラス+具象クラス) = 抽象クラス / 全体のクラス
例題2
infraモジュールの不安定度は?
https://cdn-ak.f.st-hatena.com/images/fotolife/d/delyumemori/20211122/20211122151140.png
infraモジュールの入力は5、出力は0であるので
不安定度 = 出力 / (出力+入力) = 0/5 = 0
つまり非常に安定したモジュール
例題3
appモジュールの不安定度は?
appモジュールの入力は0、出力は8であるので
不安定度 = 出力 / (出力+入力) = 8/8 = 1
つまり極めて不安定なモジュール
ちなみに以下のような原則が存在してます(後の章で紹介されるはず)
安定依存の原則(SDP) 安定度の高い方向に依存すること。
質疑応答